#include <time.h>

extern "C" 
{
	#include "lua.h"
	#include "lualib.h"
	#include "lauxlib.h"
};

#include "lua_tinker.h"

#include "common.h"

#include "object.h"
#include "asset.h"
#include "mesh.h"
#include "effect.h"
#include "body.h"
#include "assetmanager.h"

#include "scene.h"
#include "scenemanager.h"

#include "actor.h"
#include "pawn.h"
#include "actormanager.h"

#ifdef _DEBUG
#include "debugdialog.h"
#endif

#ifdef _DEBUG
#define DEBUG_NEW new( _NORMAL_BLOCK, __FILE__, __LINE__ )
#define new DEBUG_NEW
#endif

Core* Core::_pCore = NULL;

static const LPCWSTR gClassName = L"Cube";

LRESULT CALLBACK MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
	return Core::GetInstance()._pState->MessageHandler( hWnd, msg, wParam, lParam );
}

Core::Core()
:	_clientSizeX( 0 )
,	_clientSizeY( 0 )
,	_hInstance( NULL )
,	_hMainWnd( NULL )
,	_isInitialized( false )
,	_isStillPlaying( true )
,	_pState( NULL )
{
}

Core::~Core()
{
	if( _pSceneMgr )
	{
		delete _pSceneMgr;
		_pSceneMgr = NULL;
	}

}

Core& Core::GetInstance()
{
	if( _pCore == NULL )
	{
		_pCore = new Core();
		if( _pCore == NULL )
		{
			// error
			assert( false );
		}
	}

	return *_pCore;
}

/*
ActorManager& Core::GetActorManager()
{
	return *Core::GetInstance()._pActorMgr;
}

AssetManager& Core::GetAssetManager()
{
	return *Core::GetInstance()._pAssetMgr;
}
*/

SceneManager& Core::GetSceneManager()
{
	return *Core::GetInstance()._pSceneMgr;
}

bool Core::Initialize( HINSTANCE hInst )
{
	srand( static_cast< unsigned int >( time( NULL ) ) );

	_hInstance = hInst;

	_pState = new LogoState();
	if( _pState == NULL )
	{
		// error
		assert( false );
	}

	RECT winRect	= {0};
	winRect.left	= GetPrivateProfileInt( L"Settings", L"WindowsStartPosX", 0, gConfigFilePath );
	winRect.top		= GetPrivateProfileInt( L"Settings", L"WindowsStartPosY", 0, gConfigFilePath );
	_clientSizeX	= GetPrivateProfileInt( L"Settings", L"WindowsClientSizeX", 0, gConfigFilePath );
	_clientSizeY	= GetPrivateProfileInt( L"Settings", L"WindowsClientSizeY", 0, gConfigFilePath );
	winRect.right	= winRect.left + _clientSizeX;
	winRect.bottom	= winRect.top  + _clientSizeY;
	AdjustWindowRect( &winRect, WS_CAPTION, false );

	WNDCLASSEX wc = 
	{
		sizeof( WNDCLASSEX ), 
		CS_CLASSDC,
		MsgProc,
		0L, 0L,
		GetModuleHandle( NULL ),
		NULL, NULL, NULL, NULL,
		gClassName,
		NULL
	};
	RegisterClassEx( &wc );

	_hMainWnd = CreateWindow(
		gClassName, gClassName,
		WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU,
		winRect.left, winRect.top,
		winRect.right-winRect.left, winRect.bottom-winRect.top,
		GetDesktopWindow(),
		NULL,
		wc.hInstance,
		NULL );

	UnregisterClass( gClassName, wc.hInstance );

	if( ( _pD3d = Direct3DCreate9( D3D_SDK_VERSION ) ) == NULL )
	{
		// error
		assert( false );
	}

	/*
	_pActorMgr = new ActorManager;
	if( _pActorMgr == NULL )
	{
		assert( false );
	}

	_pAssetMgr = new AssetManager;
	if( _pAssetMgr == NULL )
	{
		assert( false );
	}
	*/

	_pSceneMgr = new SceneManager;
	if( _pSceneMgr == NULL || !_pSceneMgr->Initialize() )
	{
		assert( false );
	}

	_pState->Initialize();

	_isInitialized = true;

	// _DEBUG only
#ifdef _DEBUG
	_pDbgDlg = new DebugDialog;
#endif
	return true;
}

void Core::Release()
{
	if( _pSceneMgr )
	{
		_pSceneMgr->Release();
		_pSceneMgr = NULL;
	}

	AssetManager::UnregisterAll();
	ActorManager::UnregisterAll();

	if( _pState )
	{
		_pState->Release();
		_pState = NULL;
	}
	if( _pD3d )
	{
		_pD3d->Release();
		_pD3d = NULL;
	}

#ifdef _DEBUG
	if( _pDbgDlg )
	{
		delete _pDbgDlg;
		_pDbgDlg = NULL;
	}
#endif

	delete _pCore;
}

int Core::Run()
{
	assert( _isInitialized );

	ShowWindow( _hMainWnd, SW_SHOWDEFAULT );
	UpdateWindow( _hMainWnd );

	LARGE_INTEGER	freq;
	LARGE_INTEGER	startTime;
	LARGE_INTEGER	endTime;
	float			gapTime = 0;
	MSG msg = {0};

	QueryPerformanceFrequency( &freq );

	while( _isStillPlaying )
	{
		if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
		{
			TranslateMessage( &msg );
			DispatchMessage( &msg );
		}
		else
		{

#ifdef _DEBUG
			static float	elapsedTime = 0;
			static int		totalFrame = 0;
			elapsedTime += gapTime;
			++totalFrame;
			if( elapsedTime >= 1.0f )
			{
				_pDbgDlg->SetFPS( totalFrame / elapsedTime );
				totalFrame = 0;
				elapsedTime -= 1.0f;
			}
#endif
			QueryPerformanceCounter( &startTime );

			_pState->Run( &_pState, gapTime );

			QueryPerformanceCounter( &endTime );
			gapTime = static_cast< float >( ( endTime.QuadPart - startTime.QuadPart ) / ( float )freq.QuadPart );
		}
	}

	if( Core::GetInstance()._pCore )
	{
		Core::GetInstance().Release();
		Core::GetInstance()._pCore = NULL;
	}

	return 0;
}
